﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OhioState.AssetManaged;

namespace OhioState.Graphics.Proxy
{
    public class ModelProxy : SharedAsset, IDrawable
    {
        public ModelProxy(string filename) : base(filename)
        {
            this.filename = filename;
            string name = System.IO.Path.GetFileNameWithoutExtension(filename);
            this.Name = name;
            this.Material = MaterialProxy.White;
        }

        public float[] Vertices
        {
            get
            {
                if (!created)
                    ReadModel();

                float[] indices = new float[3*faces.Count];
                int index = 0;
                foreach (int vertexIndex in faces)
                {
                    indices[index++] = vertices[3 * vertexIndex];
                    indices[index++] = vertices[3 * vertexIndex + 1];
                    indices[index++] = vertices[3 * vertexIndex + 2];
                }
                return indices;
            }
        }

        public float[] TextureCoordinates
        {
            get
            {
                if (!created)
                    ReadModel();
                float[] indices = new float[2*texCoordIndices.Count];
                int index = 0;
                foreach (int texIndex in texCoordIndices)
                {
                    indices[index++] = textureCoords[2*texIndex];
                    indices[index++] = textureCoords[2*texIndex+1];
                }
                return indices;
            }
        }

        public int[] Indices
        {
            get
            {
                if (!created)
                    ReadModel();

                return this.faces.ToArray();
            }
        }

        private void ReadModel()
        {
            using (StreamReader fs = new StreamReader(filename))
            {
                string line = fs.ReadLine();
                while (line != null)
                {
                    ParseLine(line);
                    line = fs.ReadLine();
                }
            }
            created = true;
        }

        private void ParseLine(string line)
        {
            string[] tokens = line.Split(' ');
            switch (tokens[0])
            {
                case "v":
                    if (tokens.Length != 4)
                        throw new ApplicationException("Should have 3 floats and the key for vertices");
                    AddVertex(float.Parse(tokens[1]), float.Parse(tokens[2]), float.Parse(tokens[3]));
                    break;
                case "vt":
                    //if (tokens.Length != 3)
                    //    throw new ApplicationException("Should have 2 floats and the key for texture coordinates");
                    AddTextureCoord(float.Parse(tokens[1]), float.Parse(tokens[2]));
                    break;
                case "f":
                    int[] indices = new int[3];
                    int[] texCoordIndices = new int[3];
                    int index = 0;
                    string[] values = tokens[1].Split('/');
                    indices[index] = int.Parse(values[0]);
                    texCoordIndices[index] = int.Parse(values[1]);
                    index++;
                    values = tokens[2].Split('/');
                    indices[index] = int.Parse(values[0]);
                    texCoordIndices[index] = int.Parse(values[1]);
                    index++;
                    values = tokens[3].Split('/');
                    indices[index] = int.Parse(values[0]);
                    texCoordIndices[index] = int.Parse(values[1]);
                    index++;
                    //if (indices.Length != 3)
                    //    throw new ApplicationException("We only support triangles right now");
                    AddFace(indices);
                    AddTextureIndex(texCoordIndices);
                    break;
                default:
                    break;
            }
        }

        private void AddTextureIndex(int[] indices)
        {
            foreach (int index in indices)
                texCoordIndices.Add(index-1);
        }

        private void AddFace(int[] indices)
        {
            foreach (int index in indices)
                faces.Add(index-1);
        }

        private void AddTextureCoord(float u, float v)
        {
            textureCoords.Add(u);
            textureCoords.Add(v);
        }

        private void AddVertex(float x, float y, float z)
        {
            vertices.Add(x);
            vertices.Add(y);
            vertices.Add(z);
        }

        #region IDrawable Members
        public IBoundingVolume BoundingVolume
        {
            get { throw new NotImplementedException(); }
        }

        public IMaterial Material
        {
            get { return _material; }
            set
            {
                //
                // For fields that contain shared objects, follow this sequence:
                //   1. rewire the notification for the shared object
                //        this property points to.
                //   2. set the property.
                //   3. perform any internal updates.
                //   4. Implicit in step 3 would be changing this 
                //         object and hence notifying it's subscribers.
                //
                MaterialProxy mat = value as MaterialProxy;
                if (mat != null)
                {
                    IAssetManagedHandler changedEvent = new IAssetManagedHandler(FireUpdateChangedEvent);
                    this.SetPropertyEventHandling(_material, mat, changedEvent);
                    _material = mat;
                    this.FireUpdateChangedEvent(mat);
                }
            }
        }

        public void Render(IRenderPanel panel)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Member variables
        private string filename;
        IList<float> vertices = new List<float>(10000);
        IList<float> textureCoords = new List<float>(10000);
        IList<int> faces = new List<int>(10000);
        IList<int> texCoordIndices = new List<int>(10000);
        private bool created = false;

        private MaterialProxy _material;
        #endregion
    }
}
